從[Day10]~[Day13],我們將針對不同人時地事四個面向,建立初始schema。
PoliceRank共有十七個階級,其中十五個階級為依據維基百科所定義。剩餘兩個階級分別為Protected(臥底)及Cadet(警校學生)。
由於階級是有限個數,適合使用extending Enum來建立PoliceRank。
其中extending是EdgeDB中表現繼承(inheritance)的關鍵字。
scalar type PoliceRank extending enum<Protected, Cadet, PC, SPC, SGT, SSGT, PI, IP, SIP, CIP, SP, SSP, CSP, ACP, SACP, DCP, CP>;
GangsterRank共有三個階級。
scalar type GangsterRank extending enum<Nobody, Leader, Boss>;
Abstract object types不能直接被生成,但可以被其它object type所繼承。
Person作為一個抽象的人物abstract object type,目的是希望能夠被真實世界的演員(Actor)以及劇中角色(Character)所extending。不論是演員或角色,有三個property是兩者皆具備的:
name property為一必填的property,為演員或劇中角色名字。nickname property記錄綽號。eng_name property記錄英文名字。abstract type Person {
required name: str;
nickname: str;
eng_name: str;
}
IsPolice有三個property:
police_rank property代表警察官階,預設為警校學生(PoliceRank.Cadet)。object type的property或link可以使用{}於其中添加一些額外的資訊或限制,例如給予預設值。dept property代表警察部門。is_officer property是一computed property(留意is_officer後使用的是:=,不是:),會根據police_rank是否高於PoliceRank.PI,來顯示true或false(enum的值是可以比較的,所以這邊可以使用>=)。abstract type IsPolice {
police_rank: PoliceRank {
default:= PoliceRank.Cadet;
};
dept: str;
is_officer:= .police_rank >= PoliceRank.PI;
}
computed property或link不必預先定義於schema,而可以根據某些條件來動態計算。例如這邊就用來動態計算警察的官階,是否高於PoliceRank.PI這個階級。由於computed是由已知資訊所計算而來,所以EdgeDB可以推斷出型別,無須特別指定。
IsGangster有一個property及一個link:
gangster_rank property代表黑社會階級,預設為小弟(GangsterRank.Nobody)。gangster_boss link代表此角色的老大。abstract type IsGangster {
gangster_rank: GangsterRank {
default:= GangsterRank.Nobody;
};
gangster_boss: GangsterBoss;
}
EdgeDB具有多重繼承的特色,所以IsSpy可以同時extending IsPolice及IsGangster用來表示臥底。
abstract type IsSpy extending IsPolice, IsGangster;
Character extending Person用來表示劇中角色,有一個property與兩個link:
classic_lines property為一array<str>,用來記錄角色於劇中的名言。lover link為劇中角色的戀人。actors multi link為飾演劇中角色的演員(使用multi,因為一個角色可能由一個以上演員詮釋)。type Character extending Person {
classic_lines: array<str>;
lover: Character;
multi actors: Actor;
}
Actor extending Person用來表示演員。
type Actor extending Person;
Police同時extending Character及IsPolice用來表示警察。
type Police extending Character, IsPolice;
Gangster同時extending Character及IsGangster用來表示黑社會。
type Gangster extending Character, IsGangster;
GangsterBoss extending Gangster而來。
type GangsterBoss extending Gangster {
overloaded gangster_rank: GangsterRank {
default:= GangsterRank.Boss;
constraint expression on (__subject__ = GangsterRank.Boss);
};
# excluding self
constraint expression on (__subject__ != .gangster_boss) {
errmessage := "The boss can't be his/her own boss.";
}
}
當想要針對繼承而來的property或link進行更嚴格限制時,需要使用overloaded。請留意,overloaded只能進行更嚴格的限制,無法放鬆。
由於GangsterBoss也算是Gangster,只不過其gangster_rank位階較其它黑社會成員高,所以可以使用overloaded針對gangster_rank加上兩個限制:
預設其gangster_rank為GangsterRank.Boss:
default:= GangsterRank.Boss;
無論是insert或update GangsterBoss,其gangster_rank都只能指定為GangsterRank.Boss:
constraint expression on (__subject__ = GangsterRank.Boss);
constraint expression on可接受一個expression來返回true或false,如果返回的是true的話,才能允許相關操作。而__subject__代表此處將受限制的值(一個GangsterRank scalar)。
此外,我們針對GangsterBoss這個object type也加上一個constraint來限制自己不能是自己的gangster_boss。其中:
.gangster_boss的.代表引用的是自身的gangster_boss property(定義於Gangster中)。__subject__則代表自己(一個GangsterBoss object)。errmessage可以指定於insert或update發生錯誤時,所顯示的錯誤訊息。constraint expression on (__subject__ != .gangster_boss) {
errmessage := "The boss can't be his/her own boss.";
}
PoliceSpy同時extending Character及IsSpy,為警方派至黑社會之臥底。
type PoliceSpy extending Character, IsSpy;
GangsterSpy同時extending Character及IsSpy,為黑社會派至警方之臥底。
type GangsterSpy extending Character, IsSpy;